/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#pragma once

#include <baja/common.hpp>
#include <baja/math.hpp>

namespace baja { namespace app {

namespace m = baja::math;

struct resize_args
{
    int32 width;
    int32 height;
};

enum class pointer_event_type
{
    none = 0,
    down,
    move,
    up,
    wheel,
};

enum class device_type
{
    none = 0,
    mouse,
    touch,
    pen,
};

enum class pointer_button : uint32
{
    none = 0,
    left = 1,
    right = 2,
    middle = 4,
};

enum class key_modifiers : uint32
{
    none = 0,
    control = 0x1,
    menu = 0x2,
    shift = 0x4,
    windows = 0x8,
};

enum class screen_change_event_type
{
    logicalDpi,
    orientation,
};

enum class orientation
{
    portrait,
    landscape,
    portraitFlipped,
    landscapeFlipped,
};

struct screen_change_args
{
    screen_change_args(const screen_change_event_type t) : 
        type(t), orientation(orientation::portrait), logicalDpi(0.f) {}

    screen_change_event_type type;
    orientation orientation;
    float32 logicalDpi;
};

struct pointer_args
{
    pointer_args():
        buttons(0),
        deviceType(device_type::none),
        point(0.f, 0.f),
        type(pointer_event_type::none),
        timestamp(0),
        pressure(0),
        wheelDelta(0),
        eraser(false),
        barrelButtonPressed(false),
        id(0),
        keys(key_modifiers::none)
    {}

    pointer_args(const pointer_args& other)
    {
        this->buttons = other.buttons;
        this->deviceType = other.deviceType;
        this->point = other.point;
        this->type = other.type;
        this->timestamp = other.timestamp;
        this->pressure = other.pressure;
        this->wheelDelta = other.wheelDelta;
        this->eraser = other.eraser;
        this->barrelButtonPressed = other.barrelButtonPressed;
        this->id = other.id;
        this->keys = other.keys;
    }

    pointer_args(const uint32 i, const pointer_event_type t, const device_type dt) :
        deviceType(dt),
        type(t),
        point(0.f, 0.f),
        pressure(0.f),
        timestamp(0),
        eraser(false),
        barrelButtonPressed(false),
        buttons(0),
        id(i),
        keys(key_modifiers::none)
    {}

    pointer_args(
            const uint32 i,
            const uint32 bt,
            const device_type dt,
            const m::point2d p,
            const pointer_event_type ty,
            const uint64 ts,
            const float32 pr,
            const key_modifiers k,
            const int32 wd,
            const bool e,
            const bool b) :
        buttons(bt),
        deviceType(dt),
        point(p),
        type(ty),
        timestamp(ts),
        pressure(pr),
        keys(k),
        wheelDelta(wd),
        eraser(e),
        barrelButtonPressed(b),
        id(i)
    {}

    uint32 id;
    uint32 buttons;
    device_type deviceType;
    m::point2d point;
    pointer_event_type type;
    uint64 timestamp;
    float32 pressure;
    key_modifiers keys;
    int32 wheelDelta;
    bool eraser;
    bool barrelButtonPressed;
};

struct key_args
{
    key_args() : vk(0) {}

    int32 vk;
};

enum class gesture_event_type
{
    none,
    start,
    update,
    complete,
    hold,
    tap,
    doubleTap,
};

struct gesture_data
{
    gesture_data() : expansion(0), scale(0), rotation(0), translation(0.f, 0.f) {}
    gesture_data(const gesture_data& other) : expansion(other.expansion), scale(other.scale), rotation(other.rotation), translation(other.translation) {}

    float32 expansion;
    float32 scale;
    float32 rotation;
    m::point2d translation;

    bool operator==(const gesture_data& other)
    {
        return (expansion == other.expansion) && (scale == other.scale) && (rotation == other.rotation) && (translation == other.translation);
    }
};

struct gesture_args
{
    gesture_args(const gesture_event_type t, const device_type dt) : type(t), deviceType(dt), position(0.f, 0.f) {}

    device_type deviceType;
    gesture_data delta;
    gesture_data cumulative;
    gesture_event_type type;
    m::point2d position;
    m::point2d linearVelocity;
};

struct irender_observer : public ibase
{
    BAJA_INTERFACE_NOTAG(irender_observer);

    virtual void onRender() = 0;
    virtual void onScreenChange(const screen_change_args& args) = 0;
    virtual void onResize(const resize_args& args) = 0;
    virtual void onVisibilityChanged(const bool visible) = 0;
};

struct itick_observer : public ibase
{
    BAJA_INTERFACE_NOTAG(itick_observer);

    virtual void onTick() = 0;
};

struct isuspension_deferral : public ibase
{
    BAJA_INTERFACE_NOTAG(isuspension_deferral);

    virtual void complete() = 0;
};

struct isuspendable : public ibase
{
    BAJA_INTERFACE_NOTAG(isuspendable);
    virtual void onSuspend(const std::shared_ptr<isuspension_deferral>& deferral) = 0;
};

struct iinput_observer : public ibase
{
    BAJA_INTERFACE(iinput_observer, "7538348D-9687-4871-9A30-C7F900F1F8BE")

    virtual void onPointer(const pointer_args& args, void* context = nullptr) = 0;
    virtual void onGesture(const gesture_args& args, void* context = nullptr) = 0;
    virtual void onKey(const key_args& args, void* context = nullptr) = 0;
};

struct iinput_capture : public ibase
{
    BAJA_INTERFACE_NOTAG(iinput_capture);

    virtual void setCapture(const std::shared_ptr<iinput_observer>& observer) = 0;
    virtual void releaseCapture() = 0;
    virtual std::shared_ptr<iinput_observer> getCapture(void** context = nullptr) = 0;
};

struct iinput_relay : public iinput_capture
{
    BAJA_INTERFACE(iinput_relay, "B510C23D-991B-4817-B821-2F9446894222")

    virtual int32 addInputObserver(const std::shared_ptr<iinput_observer>& observer, void* context = nullptr) = 0;
    virtual void removeInputObserver(const std::shared_ptr<iinput_observer>& observer) = 0;
    virtual void removeInputObserver(const int32 cookie) = 0;
};

}} // namespace baja::app